﻿using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using MQTTnet.Extensions.ManagedClient;
using MQTTnet.Protocol;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Lynn.Infastructure.Net.MQTT
{
    public class MQTTClient<Handle> where Handle : MqttHandleAbstract
    {
        MqttFactory _factory;
        IMQTTOptions _options;
        Handle _handle;

        public MQTTClient(MqttFactory factory, IMQTTOptions options, Handle handle)
        {
            _factory = factory;
            _options = options;
            _handle = handle;
        }

        public async Task Start()
        {
            //Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
            var managedClient = _factory.CreateManagedMqttClient();
            var clientOptions = new ManagedMqttClientOptionsBuilder();
            clientOptions.WithClientOptions(o => o.WithTcpServer(_options.Server, _options.Port)
            .WithClientId(_options.ClientId)
            .WithCredentials(_options.UserName, _options.PassWord)
            .WithCleanSession(false)
            //.WithCommunicationTimeout(TimeSpan.FromSeconds(1))
            );
            await managedClient.StartAsync(clientOptions.Build());
            await managedClient.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic(_options.Topic).WithQualityOfServiceLevel(_options.Level).Build());
            managedClient.UseApplicationMessageReceivedHandler(async c =>
            {
                
                var msg = Encoding.Default.GetString(c.ApplicationMessage.Payload);
                var success = await _handle.MessageReceived(c.ApplicationMessage.Topic, msg, c.ApplicationMessage.ResponseTopic);
                if (success)
                {
                    c.IsHandled = true;
                    c.ReasonCode = MqttApplicationMessageReceivedReasonCode.Success;
                }
            });
            managedClient.UseConnectedHandler(async c =>
            {
                await _handle.Connected(c);
            });
            managedClient.UseDisconnectedHandler(async c =>
            {
                await _handle.Disconnected(c);
            });
            _handle.ManagedMqttClient = managedClient;
            var tcs = new TaskCompletionSource<object>();
            //managedClient.ConnectingFailedHandler = new ConnectingFailedHandlerDelegate(e => tcs.TrySetResult(null));
            await Task.WhenAny(Task.Delay(managedClient.Options.ClientOptions.CommunicationTimeout), tcs.Task);
            await managedClient.PingAsync(CancellationToken.None);
        }
    }
}
